import './index.css'
import './xnquery.js'

const $ = window.XNQuery;
let defaultOption = {
    on: {}
}

class XnTable {
    constructor(container, data, option) {
        this.option = $.extend(true,{}, defaultOption, option)
        this.container = container;
        this.container.classList.add('xntable')
        this.data = $.extend(true,[],data);
        this.scrollLeft = 0;
        this.translate = 0;
        this.translateIndex = 0;
        this.hover = false;
        this.init();
        this.addEvent();
        if (this.option.canResize) {
            this.addClickEvent();
        }
        if (this.option.animate.open) {
            this.addAnimate()
            this.addHoverEvent()
        }
    }

    init() {
        // this.getSize();
        this.calcColumn();
        this.calcWidth();
        this.rendDom();
    }

    calcColumn(){
        this.endHead=[];
        this.levelColumns=[];
        this.level=0;
        this._calcColumnColFunc(this.option.columns,this.level)
        // console.log(this.option.columns,this.endHead);
    }

    _calcColumnColFunc(cols,level){
        if(!this.levelColumns[level]){
            this.levelColumns[level]=[];
        }
        for(let i=0;i<cols.length;i++){
            let c=cols[i];
            this.levelColumns[level].push(c);
            if(c.children && c.children.length>0){
                c.colspan=c.children.length;
                this._calcColumnColFunc(c.children,level+1)
            }
            else{
                this.endHead.push(c)
            }
        }
        if(level>this.level){
            this.level=level;
        }
    }

    addHoverEvent() {
        this.container.addEventListener('mouseenter', e => {
            this.hover = true;
        })
        this.container.addEventListener('mouseleave', e => {
            this.hover = false;
        })
    }

    addAnimate() {
        if(!this.option.animate.open){
            return;
        }
        let time = 0;
        if (!this.hover) {
            let scrollDom = this.container.querySelector('.xntable-body')
            if (this.translateIndex == this.data.length) {
                scrollDom.style.transition = 'none';
                let a = Array.from(scrollDom.querySelectorAll('tbody tr')).slice(0, this.data.length)
                a.forEach((ele, i) => {
                    scrollDom.querySelector('tbody').appendChild(ele)
                })

                this.translate = 0;
                this.translateIndex = 0;
                scrollDom.style.transform = 'translateY(' + 0 + 'px)';
                time = 0;
            } else {
                let h = scrollDom.querySelectorAll('tbody tr')[this.translateIndex].clientHeight;
                let translateY = this.translate - h;
                let turnTime = this.option.animate.type == 'step' ? (h / this.option.animate.step) : this.option.animate.turnTime
                // scrollDom.style.transition = 'transform '+this.option.animate.turnTime+'ms linear';
                scrollDom.style.transition = 'transform ' + turnTime + 'ms linear';
                scrollDom.style.transform = 'translateY(' + translateY + 'px)';
                this.translate = translateY;
                this.translateIndex += 1;
                time = this.option.animate.sleepTime + turnTime
            }
        }
        window.setTimeout(() => {
            this.addAnimate()
        }, time)
    }

    calcWidth() {
        this.columns = {};
        this.totalWidth = 0;
        this.noWidth = [];
        for (let i = 0; i < this.endHead.length; i++) {
            let c = this.endHead[i];
            if (c.width) {
                this.totalWidth += c.width
            } else {
                this.noWidth.push(c.prop);
                c.autoWidth = true;
            }
            this.columns[c.prop] = c;
        }
        this.setWidth();
    }

    setWidth() {
        this.getSize();
        let totalWidth1 = this.size.width;
        this.noWidth = [];
        this.totalWidth = 0;
        for (let i in this.columns) {
            if (this.columns[i].autoWidth) {
                this.noWidth.push(i)
            } else {
                this.totalWidth += this.columns[i].width;
            }
        }
        let totalWidth = totalWidth1 - this.totalWidth;


        if (this.noWidth.length > 0) {
            let autoWidth = (totalWidth - this.size.scrollWidth) / this.noWidth.length;
            for (let i = 0; i < this.noWidth.length; i++) {
                // if (this.columns[this.noWidth[i]].autoWidth) {
                this.columns[this.noWidth[i]].width = autoWidth;
                // }
            }
        }

    }

    getSize() {
        this.size = {
            width: $(this.container).outerWidth(),
            height: $(this.container).outerHeight(),
            scrollWidth: this.getScrollWidth()
        }
    }

    rendDom() {
        let colgroup = '<colgroup>';
        let bodycolgroup = '';
        let header = `<thead>`;
        // let bodyheader = `<thead>`;
        let body = ''
        for(let j=0;j<this.levelColumns.length;j++){
            header+='<tr>';
            // bodyheader+='<tr>';
            for(let i=0;i<this.levelColumns[j].length;i++){
                let c=this.levelColumns[j][i];
                let overflow = c.head ? c.head.overflow : this.option.head.overflow;
                let textAlign = c.head ? c.head.textAlign : this.option.head.textAlign;
                let style = this.setCssHead(c.head)
                let colspan=c.colspan;
                let rowspan=1;
                let resizer='';
                if(!c.children || c.children.length<=0){
                    rowspan=this.level-j+1;
                    resizer='<div class="xntable-resizebar"></div>'
                }
                let sort=c.openSort?`<div class="bi-table-sort"><span class="iconfont-bi icon-bizhankai1-copy"></span><span class="iconfont-bi icon-bizhankai1-copy"></span></div>`:''
                let head = `<th style="${style}" data-prop="${c.prop}" class="${overflow} ${textAlign} ${c.openSort ? 'bi-table-sort-th' : ''} bi-table-sort-${c.sort}" colspan="${colspan}" rowspan="${rowspan}"><div class="xntable-cell"><div class="xntable-inner-div">${c.name}</div>${sort}${resizer}</div></th>`
                header+=head;
                // bodyheader+=head;
            }
            if(j==0){
                header+='<th rowspan="'+(this.level+1)+'"><\/th></tr>'}
            else{
                header+='</tr>'
            }
            // bodyheader+='</tr>'
        }
        header += `<\/thead>`
        // bodyheader += `<\/thead>`
        for (let i = 0; i < this.endHead.length; i++) {
            let c = this.endHead[i];
            colgroup += '<col width="' + c.width + 'px" data-prop="' + c.prop + '">'
        }
        bodycolgroup += colgroup + '<\/colgroup>'
        colgroup += '<col width="' + this.size.scrollWidth + 'px" data-prop="' + '$$xngap' + '"><\/colgroup>'
        for (let j = 0; j < this.data.length; j++) {
            let d = this.data[j];
            body += '<tr>'
            for (let i = 0; i < this.endHead.length; i++) {
                let c = this.endHead[i];
                let overflow = c.body ? c.body.overflow : this.option.body.overflow;
                let textAlign = c.body ? c.body.textAlign : this.option.body.textAlign;
                let style = this.setCssBody(c.body, j)
                body += `<td data-prop="${c.prop}" style="${style}" data-index="${j}" class="${overflow} ${textAlign}"><div class="xntable-cell"><div class="xntable-inner-div">${d[c.prop]}</div><div class="xntable-resizebar"></div></td>`
            }
            body += '</tr>'
        }
        if (this.option.animate.open) {
            body += body;
        }

        let resizebar = '<div class="resizebar-highlight"></div>'
        let {headstyle, bodystyle} = this.setCssOuterBorder();
        let html = `<div class="${this.option.border} ${this.option.animate.open ? 'animate' : ''} ${this.option.animate.canScroll ? 'scroll' : ''}">
<div class="xntable-head-container">
        <table class="xntable-head" style="${headstyle}">
            ${colgroup}
            ${this.option.hideHead?'':header}
        </table>
        ${resizebar}
        </div>
        <div class="xntable-body-container">
        <table class="xntable-body" style="${bodystyle}">
            ${bodycolgroup}
            <tbody>
                ${body}
            </tbody>
        </table>
        </div>
        
        </div>
        `
        this.container.innerHTML = html;
        // this.container.classList.add(this.option.border)
        this.resizer = this.container.querySelector('.resizebar-highlight')
        this.addScrollEvent();
        // if (this.noWidth.length <= 0) {
        this.container.querySelector('.xntable-body').style.width = this.totalWidth + 'px'
        this.container.querySelector('.xntable-head').style.width = this.totalWidth + 'px'
        // }
        this.setHeight()
    }
    setHeight(){
        let height=this.container.querySelector('.xntable-head').clientHeight;
        this.container.querySelector('.xntable-body-container').style.height='calc( 100% - '+(height)+'px)'
    }

    setCssOuterBorder() {
        if(this.option.border!='normal'){
            return {headstyle:'', bodystyle:''}
        }
        let headcss = this.option.head
        let headstyle = `
            border-top:${headcss.borderWidth}px solid ${headcss.borderColor};
            border-left:${headcss.borderWidth}px solid ${headcss.borderColor};
        `

        let bodycss = this.option.body
        let bodystyle = `
            border-top:${bodycss.borderWidth}px solid ${bodycss.borderColor};
            border-left:${bodycss.borderWidth}px solid ${bodycss.borderColor};
        `
        return {headstyle, bodystyle}
    }

    setCssHead(css1) {
        let css = $.extend(true, {}, this.option.head, css1)
        let style = `
        font-size:${css.fontSize}px;
        line-height:${css.lineHeight}px;
        border-bottom:${css.borderWidth}px solid ${css.borderColor};
        border-right:${css.borderWidth}px solid ${css.borderColor};
        background:${css.background};
        color:${css.color};
        `
        return style;
    }

    setCssBody(css1, index) {
        let css = $.extend(true, {}, this.option.body, css1)
        let background = this.option.body.background[index % this.option.body.background.length];
        if (css1 && css1.background) {
            background = css1.background[index % css1.background.length];
        }

        let style = `
        font-size:${css.fontSize}px;
        line-height:${css.lineHeight}px;
        border-bottom:${css.borderWidth}px solid ${css.borderColor};
        border-right:${css.borderWidth}px solid ${css.borderColor};
        background:${background};
        color:${css.color};
        `
        return style;
    }

    getScrollWidth() {
        var noScroll, scroll, oDiv = document.createElement("DIV");
        oDiv.style.cssText = "position:absolute; top:-1000px; width:100px; height:100px; overflow:hidden;";
        noScroll = document.body.appendChild(oDiv).clientWidth;
        oDiv.style.overflowY = "scroll";
        scroll = oDiv.clientWidth;
        document.body.removeChild(oDiv);
        return noScroll - scroll;
    }

    addClickEvent() {
        this.clickFunc=e=>{
            let $t = $(e.target);
            if ($t.parents('td').get(0) || $t.get(0).nodeName == 'td') {
                let $ele = $t.get(0).nodeName == 'td' ? $t : $t.parents('td').eq(0)
                let prop = $ele.attr('data-prop')
                let i = $ele.attr('data-index');
                let data = this.data[i];
                let value = this.data[i][prop]
                let target = 'body'
                if (this.option.on.clickBody) {
                    this.option.on.clickBody({data: data, value: value, target: target, prop: prop},e)
                }
            }
            if ($t.parents('th').get(0) || $t.get(0).nodeName == 'th') {
                let $ele = $t.get(0).nodeName == 'th' ? $t : $t.parents('th').eq(0)
                let prop = $ele.attr('data-prop')
                let target = 'head'
                if (this.option.on.clickHead) {
                    this.option.on.clickHead({target: target, prop: prop})
                }
                let col=this.getColByProp(prop);
                if(col.openSort){
                    let nextsort = col.sort == 'desc' ? 'asc' : (col.sort == 'asc' ? '' : 'desc');
                    if (this.option.on.changeSort) {
                        this.option.on.changeSort(
                            {prop: col.prop,
                             nextsort: nextsort})
                    }
                }
            }
        }
        this.container.addEventListener('click', this.clickFunc)
    }
    refresh(){
        this.setWidth();
        this.rendDom();
    }
    addEvent() {
        let ele = {}
        let down = false
        this.mousedownfunc = e => {
            let $t = $(e.target);
            if ($t.hasClass('xntable-resizebar')) {
                e.stopPropagation();
                down = true;
                ele.prop = ($t.parents('th').get(0)) ? ($t.parents('th').get(0).getAttribute('data-prop')) : ($t.parents('td').get(0).getAttribute('data-prop'))
                ele.x = e.clientX;
                ele.col = this.getColByProp(ele.prop);
            }
        }
        this.container.addEventListener('mousedown', this.mousedownfunc)
        this.mousemovefunc = e => {
            if (down) {
                e.stopPropagation();
                e.preventDefault();
                let left = e.clientX - this.container.getBoundingClientRect().left;
                let offsetX = e.clientX - ele.x;
                let col = ele.col;
                let newWidth = col.width + offsetX;
                if (newWidth < (col.minWidth || 42)) {
                    left = (col.minWidth || 42) - col.width + ele.x - this.container.getBoundingClientRect().left;
                }
                this.resizer.style.display = 'block'
                this.resizer.style.left = left + this.scrollLeft + 'px'
            }
        }
        document.addEventListener('mousemove', this.mousemovefunc)

        this.mouseupfunc = e => {
            if (down) {
                let offsetX = e.clientX - ele.x;
                let col = ele.col;
                let newWidth = col.width + offsetX;
                if (newWidth < (col.minWidth || 42)) {
                    col.width = (col.minWidth || 42);
                } else {
                    col.width = newWidth;

                }
                col.autoWidth = false;
                if (this.option.on.changeWidth) {
                    this.option.on.changeWidth(col)
                }
                this.refresh();
                down = false;
                this.resizer.style.display = 'none'
            }
        }
        document.addEventListener('mouseup', this.mouseupfunc)
        this.dblfunc = e => {
            let $t = $(e.target);
            if ($t.hasClass('xntable-inner-div')) {
                let prop = $t.parents('th').get(0).getAttribute('data-prop')
                let col = this.getColByProp(prop);
                col.autoWidth = true;
                if (this.option.on.changeWidth) {
                    this.option.on.changeWidth(col)
                }
                this.refresh();

            }
        }
        this.container.addEventListener('dblclick', this.dblfunc)
        this.resizefunc = e => {

            this.refresh();
        }
        window.addEventListener('resize', this.resizefunc)

    }
    destory(){
        window.removeEventListener('resize',this.resizefunc)
        this.container.removeEventListener('dblclick', this.dblfunc)
        document.removeEventListener('mousemove', this.mousemovefunc)
        document.removeEventListener('mouseup', this.mouseupfunc)
        this.container.removeEventListener('mousedown', this.mousedownfunc)
        this.container.removeEventListener('click', this.clickFunc)
        this.option.animate.open=false;
    }

    addScrollEvent() {
        this.container.querySelector('.xntable-head-container').scrollLeft = this.scrollLeft;
        this.container.querySelector('.xntable-body-container').scrollLeft = this.scrollLeft;
        this.scrollfunc = e => {
            this.scrollLeft = (this.container.querySelector('.xntable-body-container').scrollLeft) || 0;
            this.container.querySelector('.xntable-head-container').scrollLeft = this.scrollLeft;
        }
        this.container.querySelector('.xntable-body-container').addEventListener('scroll', this.scrollfunc)
    }

    getColByProp(prop) {
        return this.columns[prop]
    }
}

export default XnTable
